/* *
 *
 *  (c) 2010-2019 Torstein Honsi
 *
 *  License: www.highcharts.com/license
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */
"use strict";
import H from "../parts/Globals.js";
import U from "../parts/Utilities.js";
var defined = U.defined,
  extend = U.extend,
  isArray = U.isArray,
  isNumber = U.isNumber,
  pick = U.pick;
import "../parts/Options.js";
import "../parts/Series.js";
var noop = H.noop,
  Series = H.Series,
  seriesType = H.seriesType,
  seriesTypes = H.seriesTypes,
  seriesProto = Series.prototype,
  pointProto = H.Point.prototype;
/**
 * The area range series is a carteseian series with higher and lower values for
 * each point along an X axis, where the area between the values is shaded.
 *
 * @sample {highcharts} highcharts/demo/arearange/
 *         Area range chart
 * @sample {highstock} stock/demo/arearange/
 *         Area range chart
 *
 * @extends      plotOptions.area
 * @product      highcharts highstock
 * @excluding    stack, stacking
 * @requires     highcharts-more
 * @optionparent plotOptions.arearange
 */
seriesType(
  "arearange",
  "area",
  {
    /**
     * Whether to apply a drop shadow to the graph line. Since 2.3 the shadow
     * can be an object configuration containing `color`, `offsetX`, `offsetY`,
     * `opacity` and `width`.
     *
     * @type      {boolean|Highcharts.ShadowOptionsObject}
     * @product   highcharts
     * @apioption plotOptions.arearange.shadow
     */
    /**
     * @default   low
     * @apioption plotOptions.arearange.colorKey
     */
    /**
     * Pixel width of the arearange graph line.
     *
     * @since 2.3.0
     *
     * @private
     */
    lineWidth: 1,
    threshold: null,
    tooltip: {
      pointFormat:
        '<span style="color:{series.color}">\u25CF</span> ' +
        "{series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>",
    },
    /**
     * Whether the whole area or just the line should respond to mouseover
     * tooltips and other mouse or touch events.
     *
     * @since 2.3.0
     *
     * @private
     */
    trackByArea: true,
    /**
     * Extended data labels for range series types. Range series data labels use
     * no `x` and `y` options. Instead, they have `xLow`, `xHigh`, `yLow` and
     * `yHigh` options to allow the higher and lower data label sets
     * individually.
     *
     * @declare Highcharts.SeriesAreaRangeDataLabelsOptionsObject
     * @exclude x, y
     * @since   2.3.0
     * @product highcharts highstock
     *
     * @private
     */
    dataLabels: {
      align: null,
      verticalAlign: null,
      /**
       * X offset of the lower data labels relative to the point value.
       *
       * @sample highcharts/plotoptions/arearange-datalabels/
       *         Data labels on range series
       * @sample highcharts/plotoptions/arearange-datalabels/
       *         Data labels on range series
       */
      xLow: 0,
      /**
       * X offset of the higher data labels relative to the point value.
       *
       * @sample highcharts/plotoptions/arearange-datalabels/
       *         Data labels on range series
       */
      xHigh: 0,
      /**
       * Y offset of the lower data labels relative to the point value.
       *
       * @sample highcharts/plotoptions/arearange-datalabels/
       *         Data labels on range series
       */
      yLow: 0,
      /**
       * Y offset of the higher data labels relative to the point value.
       *
       * @sample highcharts/plotoptions/arearange-datalabels/
       *         Data labels on range series
       */
      yHigh: 0,
    },
    // Prototype members
  },
  {
    pointArrayMap: ["low", "high"],
    pointValKey: "low",
    deferTranslatePolar: true,
    /* eslint-disable valid-jsdoc */
    /**
     * @private
     */
    toYData: function (point) {
      return [point.low, point.high];
    },
    /**
     * Translate a point's plotHigh from the internal angle and radius measures
     * to true plotHigh coordinates. This is an addition of the toXY method
     * found in Polar.js, because it runs too early for arearanges to be
     * considered (#3419).
     * @private
     */
    highToXY: function (point) {
      // Find the polar plotX and plotY
      var chart = this.chart,
        xy = this.xAxis.postTranslate(
          point.rectPlotX,
          this.yAxis.len - point.plotHigh
        );
      point.plotHighX = xy.x - chart.plotLeft;
      point.plotHigh = xy.y - chart.plotTop;
      point.plotLowX = point.plotX;
    },
    /**
     * Translate data points from raw values x and y to plotX and plotY.
     * @private
     */
    translate: function () {
      var series = this,
        yAxis = series.yAxis,
        hasModifyValue = !!series.modifyValue;
      seriesTypes.area.prototype.translate.apply(series);
      // Set plotLow and plotHigh
      series.points.forEach(function (point) {
        var high = point.high,
          plotY = point.plotY;
        if (point.isNull) {
          point.plotY = null;
        } else {
          point.plotLow = plotY;
          point.plotHigh = yAxis.translate(
            hasModifyValue ? series.modifyValue(high, point) : high,
            0,
            1,
            0,
            1
          );
          if (hasModifyValue) {
            point.yBottom = point.plotHigh;
          }
        }
      });
      // Postprocess plotHigh
      if (this.chart.polar) {
        this.points.forEach(function (point) {
          series.highToXY(point);
          point.tooltipPos = [
            (point.plotHighX + point.plotLowX) / 2,
            (point.plotHigh + point.plotLow) / 2,
          ];
        });
      }
    },
    /**
     * Extend the line series' getSegmentPath method by applying the segment
     * path to both lower and higher values of the range.
     * @private
     */
    getGraphPath: function (points) {
      var highPoints = [],
        highAreaPoints = [],
        i,
        getGraphPath = seriesTypes.area.prototype.getGraphPath,
        point,
        pointShim,
        linePath,
        lowerPath,
        options = this.options,
        connectEnds = this.chart.polar && options.connectEnds !== false,
        connectNulls = options.connectNulls,
        step = options.step,
        higherPath,
        higherAreaPath;
      points = points || this.points;
      i = points.length;
      // Create the top line and the top part of the area fill. The area fill
      // compensates for null points by drawing down to the lower graph,
      // moving across the null gap and starting again at the lower graph.
      i = points.length;
      while (i--) {
        point = points[i];
        if (
          !point.isNull &&
          !connectEnds &&
          !connectNulls &&
          (!points[i + 1] || points[i + 1].isNull)
        ) {
          highAreaPoints.push({
            plotX: point.plotX,
            plotY: point.plotY,
            doCurve: false, // #5186, gaps in areasplinerange fill
          });
        }
        pointShim = {
          polarPlotY: point.polarPlotY,
          rectPlotX: point.rectPlotX,
          yBottom: point.yBottom,
          // plotHighX is for polar charts
          plotX: pick(point.plotHighX, point.plotX),
          plotY: point.plotHigh,
          isNull: point.isNull,
        };
        highAreaPoints.push(pointShim);
        highPoints.push(pointShim);
        if (
          !point.isNull &&
          !connectEnds &&
          !connectNulls &&
          (!points[i - 1] || points[i - 1].isNull)
        ) {
          highAreaPoints.push({
            plotX: point.plotX,
            plotY: point.plotY,
            doCurve: false, // #5186, gaps in areasplinerange fill
          });
        }
      }
      // Get the paths
      lowerPath = getGraphPath.call(this, points);
      if (step) {
        if (step === true) {
          step = "left";
        }
        options.step = {
          left: "right",
          center: "center",
          right: "left",
        }[step]; // swap for reading in getGraphPath
      }
      higherPath = getGraphPath.call(this, highPoints);
      higherAreaPath = getGraphPath.call(this, highAreaPoints);
      options.step = step;
      // Create a line on both top and bottom of the range
      linePath = [].concat(lowerPath, higherPath);
      // For the area path, we need to change the 'move' statement
      // into 'lineTo' or 'curveTo'
      if (!this.chart.polar && higherAreaPath[0] === "M") {
        higherAreaPath[0] = "L"; // this probably doesn't work for spline
      }
      this.graphPath = linePath;
      this.areaPath = lowerPath.concat(higherAreaPath);
      // Prepare for sideways animation
      linePath.isArea = true;
      linePath.xMap = lowerPath.xMap;
      this.areaPath.xMap = lowerPath.xMap;
      return linePath;
    },
    /**
     * Extend the basic drawDataLabels method by running it for both lower and
     * higher values.
     * @private
     */
    drawDataLabels: function () {
      var data = this.points,
        length = data.length,
        i,
        originalDataLabels = [],
        dataLabelOptions = this.options.dataLabels,
        point,
        up,
        inverted = this.chart.inverted,
        upperDataLabelOptions,
        lowerDataLabelOptions;
      // Split into upper and lower options. If data labels is an array, the
      // first element is the upper label, the second is the lower.
      //
      // TODO: We want to change this and allow multiple labels for both upper
      // and lower values in the future - introducing some options for which
      // point value to use as Y for the dataLabel, so that this could be
      // handled in Series.drawDataLabels. This would also improve performance
      // since we now have to loop over all the points multiple times to work
      // around the data label logic.
      if (isArray(dataLabelOptions)) {
        if (dataLabelOptions.length > 1) {
          upperDataLabelOptions = dataLabelOptions[0];
          lowerDataLabelOptions = dataLabelOptions[1];
        } else {
          upperDataLabelOptions = dataLabelOptions[0];
          lowerDataLabelOptions = { enabled: false };
        }
      } else {
        // Make copies
        upperDataLabelOptions = extend({}, dataLabelOptions);
        upperDataLabelOptions.x = dataLabelOptions.xHigh;
        upperDataLabelOptions.y = dataLabelOptions.yHigh;
        lowerDataLabelOptions = extend({}, dataLabelOptions);
        lowerDataLabelOptions.x = dataLabelOptions.xLow;
        lowerDataLabelOptions.y = dataLabelOptions.yLow;
      }
      // Draw upper labels
      if (upperDataLabelOptions.enabled || this._hasPointLabels) {
        // Set preliminary values for plotY and dataLabel
        // and draw the upper labels
        i = length;
        while (i--) {
          point = data[i];
          if (point) {
            up = upperDataLabelOptions.inside
              ? point.plotHigh < point.plotLow
              : point.plotHigh > point.plotLow;
            point.y = point.high;
            point._plotY = point.plotY;
            point.plotY = point.plotHigh;
            // Store original data labels and set preliminary label
            // objects to be picked up in the uber method
            originalDataLabels[i] = point.dataLabel;
            point.dataLabel = point.dataLabelUpper;
            // Set the default offset
            point.below = up;
            if (inverted) {
              if (!upperDataLabelOptions.align) {
                upperDataLabelOptions.align = up ? "right" : "left";
              }
            } else {
              if (!upperDataLabelOptions.verticalAlign) {
                upperDataLabelOptions.verticalAlign = up ? "top" : "bottom";
              }
            }
          }
        }
        this.options.dataLabels = upperDataLabelOptions;
        if (seriesProto.drawDataLabels) {
          // #1209:
          seriesProto.drawDataLabels.apply(this, arguments);
        }
        // Reset state after the upper labels were created. Move
        // it to point.dataLabelUpper and reassign the originals.
        // We do this here to support not drawing a lower label.
        i = length;
        while (i--) {
          point = data[i];
          if (point) {
            point.dataLabelUpper = point.dataLabel;
            point.dataLabel = originalDataLabels[i];
            delete point.dataLabels;
            point.y = point.low;
            point.plotY = point._plotY;
          }
        }
      }
      // Draw lower labels
      if (lowerDataLabelOptions.enabled || this._hasPointLabels) {
        i = length;
        while (i--) {
          point = data[i];
          if (point) {
            up = lowerDataLabelOptions.inside
              ? point.plotHigh < point.plotLow
              : point.plotHigh > point.plotLow;
            // Set the default offset
            point.below = !up;
            if (inverted) {
              if (!lowerDataLabelOptions.align) {
                lowerDataLabelOptions.align = up ? "left" : "right";
              }
            } else {
              if (!lowerDataLabelOptions.verticalAlign) {
                lowerDataLabelOptions.verticalAlign = up ? "bottom" : "top";
              }
            }
          }
        }
        this.options.dataLabels = lowerDataLabelOptions;
        if (seriesProto.drawDataLabels) {
          seriesProto.drawDataLabels.apply(this, arguments);
        }
      }
      // Merge upper and lower into point.dataLabels for later destroying
      if (upperDataLabelOptions.enabled) {
        i = length;
        while (i--) {
          point = data[i];
          if (point) {
            point.dataLabels = [point.dataLabelUpper, point.dataLabel].filter(
              function (label) {
                return !!label;
              }
            );
          }
        }
      }
      // Reset options
      this.options.dataLabels = dataLabelOptions;
    },
    alignDataLabel: function () {
      seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
    },
    drawPoints: function () {
      var series = this,
        pointLength = series.points.length,
        point,
        i;
      // Draw bottom points
      seriesProto.drawPoints.apply(series, arguments);
      // Prepare drawing top points
      i = 0;
      while (i < pointLength) {
        point = series.points[i];
        // Save original props to be overridden by temporary props for top
        // points
        point.origProps = {
          plotY: point.plotY,
          plotX: point.plotX,
          isInside: point.isInside,
          negative: point.negative,
          zone: point.zone,
          y: point.y,
        };
        point.lowerGraphic = point.graphic;
        point.graphic = point.upperGraphic;
        point.plotY = point.plotHigh;
        if (defined(point.plotHighX)) {
          point.plotX = point.plotHighX;
        }
        point.y = point.high;
        point.negative = point.high < (series.options.threshold || 0);
        point.zone = series.zones.length && point.getZone();
        if (!series.chart.polar) {
          point.isInside = point.isTopInside =
            typeof point.plotY !== "undefined" &&
            point.plotY >= 0 &&
            point.plotY <= series.yAxis.len && // #3519
            point.plotX >= 0 &&
            point.plotX <= series.xAxis.len;
        }
        i++;
      }
      // Draw top points
      seriesProto.drawPoints.apply(series, arguments);
      // Reset top points preliminary modifications
      i = 0;
      while (i < pointLength) {
        point = series.points[i];
        point.upperGraphic = point.graphic;
        point.graphic = point.lowerGraphic;
        extend(point, point.origProps);
        delete point.origProps;
        i++;
      }
    },
    /* eslint-enable valid-jsdoc */
    setStackedPoints: noop,
  },
  {
    /**
     * Range series only. The high or maximum value for each data point.
     * @name Highcharts.Point#high
     * @type {number|undefined}
     */
    /**
     * Range series only. The low or minimum value for each data point.
     * @name Highcharts.Point#low
     * @type {number|undefined}
     */
    /* eslint-disable valid-jsdoc */
    /**
     * @private
     */
    setState: function () {
      var prevState = this.state,
        series = this.series,
        isPolar = series.chart.polar;
      if (!defined(this.plotHigh)) {
        // Boost doesn't calculate plotHigh
        this.plotHigh = series.yAxis.toPixels(this.high, true);
      }
      if (!defined(this.plotLow)) {
        // Boost doesn't calculate plotLow
        this.plotLow = this.plotY = series.yAxis.toPixels(this.low, true);
      }
      if (series.stateMarkerGraphic) {
        series.lowerStateMarkerGraphic = series.stateMarkerGraphic;
        series.stateMarkerGraphic = series.upperStateMarkerGraphic;
      }
      // Change state also for the top marker
      this.graphic = this.upperGraphic;
      this.plotY = this.plotHigh;
      if (isPolar) {
        this.plotX = this.plotHighX;
      }
      // Top state:
      pointProto.setState.apply(this, arguments);
      this.state = prevState;
      // Now restore defaults
      this.plotY = this.plotLow;
      this.graphic = this.lowerGraphic;
      if (isPolar) {
        this.plotX = this.plotLowX;
      }
      if (series.stateMarkerGraphic) {
        series.upperStateMarkerGraphic = series.stateMarkerGraphic;
        series.stateMarkerGraphic = series.lowerStateMarkerGraphic;
        // Lower marker is stored at stateMarkerGraphic
        // to avoid reference duplication (#7021)
        series.lowerStateMarkerGraphic = void 0;
      }
      pointProto.setState.apply(this, arguments);
    },
    haloPath: function () {
      var isPolar = this.series.chart.polar,
        path = [];
      // Bottom halo
      this.plotY = this.plotLow;
      if (isPolar) {
        this.plotX = this.plotLowX;
      }
      if (this.isInside) {
        path = pointProto.haloPath.apply(this, arguments);
      }
      // Top halo
      this.plotY = this.plotHigh;
      if (isPolar) {
        this.plotX = this.plotHighX;
      }
      if (this.isTopInside) {
        path = path.concat(pointProto.haloPath.apply(this, arguments));
      }
      return path;
    },
    destroyElements: function () {
      var graphics = ["lowerGraphic", "upperGraphic"];
      graphics.forEach(function (graphicName) {
        if (this[graphicName]) {
          this[graphicName] = this[graphicName].destroy();
        }
      }, this);
      // Clear graphic for states, removed in the above each:
      this.graphic = null;
      return pointProto.destroyElements.apply(this, arguments);
    },
    isValid: function () {
      return isNumber(this.low) && isNumber(this.high);
    },
    /* eslint-enable valid-jsdoc */
  }
);
/**
 * A `arearange` series. If the [type](#series.arearange.type) option is not
 * specified, it is inherited from [chart.type](#chart.type).
 *
 *
 * @extends   series,plotOptions.arearange
 * @excluding dataParser, dataURL, stack, stacking
 * @product   highcharts highstock
 * @requires  highcharts-more
 * @apioption series.arearange
 */
/**
 * An array of data points for the series. For the `arearange` series type,
 * points can be given in the following ways:
 *
 * 1.  An array of arrays with 3 or 2 values. In this case, the values
 *     correspond to `x,low,high`. If the first value is a string, it is
 *     applied as the name of the point, and the `x` value is inferred.
 *     The `x` value can also be omitted, in which case the inner arrays
 *     should be of length 2\. Then the `x` value is automatically calculated,
 *     either starting at 0 and incremented by 1, or from `pointStart`
 *     and `pointInterval` given in the series options.
 *     ```js
 *     data: [
 *         [0, 8, 3],
 *         [1, 1, 1],
 *         [2, 6, 8]
 *     ]
 *     ```
 *
 * 2.  An array of objects with named values. The following snippet shows only a
 *     few settings, see the complete options set below. If the total number of
 *     data points exceeds the series'
 *     [turboThreshold](#series.arearange.turboThreshold),
 *     this option is not available.
 *     ```js
 *     data: [{
 *         x: 1,
 *         low: 9,
 *         high: 0,
 *         name: "Point2",
 *         color: "#00FF00"
 *     }, {
 *         x: 1,
 *         low: 3,
 *         high: 4,
 *         name: "Point1",
 *         color: "#FF00FF"
 *     }]
 *     ```
 *
 * @sample {highcharts} highcharts/series/data-array-of-arrays/
 *         Arrays of numeric x and y
 * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
 *         Arrays of datetime x and y
 * @sample {highcharts} highcharts/series/data-array-of-name-value/
 *         Arrays of point.name and y
 * @sample {highcharts} highcharts/series/data-array-of-objects/
 *         Config objects
 *
 * @type      {Array<Array<(number|string),number>|Array<(number|string),number,number>|*>}
 * @extends   series.line.data
 * @excluding marker, y
 * @product   highcharts highstock
 * @apioption series.arearange.data
 */
/**
 * @extends   series.arearange.dataLabels
 * @product   highcharts highstock
 * @apioption series.arearange.data.dataLabels
 */
/**
 * The high or maximum value for each data point.
 *
 * @type      {number}
 * @product   highcharts highstock
 * @apioption series.arearange.data.high
 */
/**
 * The low or minimum value for each data point.
 *
 * @type      {number}
 * @product   highcharts highstock
 * @apioption series.arearange.data.low
 */
(""); // adds doclets above to tranpiled file
